package com.meevle.dao;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.red5.core.ApplicationSettings;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;

import com.meevle.business.PersistenceManagerImpl;
import com.meevle.utilities.DaoToVoAdapter;
import com.meevle.utilities.HibernateManager;
import com.meevle.vo.SlotVO;

@Entity
public class SlotDAO implements Serializable{

	private static final long serialVersionUID = 1L;
	private static Logger log = Red5LoggerFactory.getLogger(SlotDAO.class, "Meevle");
	
	private long id;
	
	private UserDAO owner;
	
	private List<SetDAO> projects = new ArrayList<SetDAO>();
	
	private List<SlotCommentDAO> comments = new ArrayList<SlotCommentDAO>();
	
	private String name;
	private Timestamp timeCreated;
	private String filename;
	private String directory;
	private int slotIndex;
	
	public SlotDAO()
	{}
	
	/**
	 * Creates a new TrackObject that instatiates tha owner, the filename and the directory
	 * @param user the user which would be owner of the track
	 */
	public SlotDAO(UserDAO user)
	{
		this(user, null, null, -1);
	}
	
	public SlotDAO(UserDAO user, Timestamp timeCreated, String name, int index)
	{
		setOwner(user);
		setTimeCreated(timeCreated);
		setName(name);
		setSlotIndex(index);
		//sets the filename and the directory of the recorded and saved track
		setFilename( String.valueOf( System.currentTimeMillis() )+".mp3" );
		setDirectory(ApplicationSettings.getInstance().getUserTracksPath(user.getUsername()));
	}

	@Id @GeneratedValue
	public long getId() {
		return id;
	}

	private void setId(long id) {
		this.id = id;
	}

	@ManyToOne(cascade={CascadeType.MERGE, CascadeType.REFRESH})
	public UserDAO getOwner() {
		return owner;
	}

	public void setOwner(UserDAO owner) {
		this.owner = owner;
	}

	@ManyToMany(cascade={CascadeType.MERGE, CascadeType.REFRESH})
	@JoinTable(	
			name = "SlotDAO_SetDAO",
			joinColumns = {@JoinColumn(name="slot_id")},
			inverseJoinColumns = {@JoinColumn(name = "set_id")}
				)
	public List<SetDAO> getSets() {
		return projects;
	}
	
	public void setSets(List<SetDAO> projects) {
		this.projects = projects;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Date getTimeCreated() {
		return timeCreated;
	}

	public void setTimeCreated(Timestamp timeCreated) {
		this.timeCreated = timeCreated;
	}

	public String getFilename() {
		return filename;
	}

	public void setFilename(String filename) {
		this.filename = filename;
	}

	public String getDirectory() {
		return directory;
	}

	public void setDirectory(String directory) {
		this.directory = directory;
	}
	
	public int getSlotIndex() {
		return slotIndex;
	}
	
	public void setSlotIndex(int index) {
		this.slotIndex = index;
	}
	
	@OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, mappedBy="slot")
	public List<SlotCommentDAO> getComments() {
		return comments;
	}
	
	public void setComments(List<SlotCommentDAO> comments) {
		this.comments = comments;
	}
	
	/**
	 * Get all the projects that feature this track
	 * @param trackVo
	 * @return
	 * @throws IllegalArgumentException if this track doesn't exist in the DB
	 * @throws Exception If something went wrong during the DB access
	 */
	public static List<SetDAO> getSetsBySlot(SlotVO trackVo) throws IllegalArgumentException, Exception
	{
		List<SetDAO> projectDaos = new ArrayList<SetDAO>();
		SlotDAO track;
		try {
			track = getSlot(trackVo);
		} catch (IllegalArgumentException e) {
			throw e;
		} catch (Exception e) {
			throw e;
		}
		projectDaos = track.getSets();
		return projectDaos;
	}
	
	/**
	 * Get a com.meevle.dao.TrackDAO object from a com.meevle.vo.TrackVO object
	 * @param trackVo
	 * @param userVo
	 * @return a com.meevle.dao.TrackObject if such a combination of track and owner was found.
	 * @throws IllegalArgumentException if the combination trackname and ownername doesn't exist in the DB
	 * @throws Exception If something went wrong during the DB access
	 */
	public static SlotDAO getSlot(SlotVO trackVo) throws IllegalArgumentException, Exception
	{
		SlotDAO track;
		try {
			track = PersistenceManagerImpl.getSlot(trackVo);
		} catch (IllegalArgumentException e) {
			throw e;
		} catch (Exception e) {
			throw e;
		}
//		track = (TrackDAO)HibernateManager.getInstance().getSessionFactory().getCurrentSession()
//		.createQuery("from TrackDAO track where name = :name AND owner.username = :username")
//		.setString("name", trackVo.getName() )
//		.setString("username", trackVo.getOwner().getUsername())
//		.uniqueResult();
//		
		if(track==null)
		{
			log.error("Someone is using a track that doesn't exist with name:\n" + trackVo.getName() );
			throw new IllegalArgumentException("This combination of track " + trackVo + " and owner " +trackVo.getOwner()+ " does not exist");
		}
		
		return track;
	}
	
	/**
	 * Get a list of all the tracks in the database.
	 * @return a <code>List</code> of all tracks
	 */
	public static List<SlotDAO> getAll()throws Exception
	{
		List<SlotDAO> ret = new ArrayList<SlotDAO>();
		try {
			ret = PersistenceManagerImpl.getAllSlots();
		} catch (Exception e) {
			throw e;
		}
		return ret;
	}
	
	/**
	 * Inserts a track in the backing database
	 * @param track The track that will be added to the database
	 * @throws Exception if during the adding to the DB the inserting failed
	 */
	public static void persist(SlotDAO track)throws Exception
	{
		try{
			UserDAO.getUser(DaoToVoAdapter.getInstance().getUserFromUserDAO( track.getOwner() ) );
			PersistenceManagerImpl.persist(track);
////			Session session = HibernateManager.getInstance().getSessionFactory().openSession();
//			Session session = HibernateManager.getInstance().getSession();
//			session.beginTransaction();
//			session.saveOrUpdate(track);
//			session.getTransaction().commit();
////			session.close();
		}catch(Exception e)
		{
			log.error("Persisting new track failed\n" + e.getMessage());
			throw e;
		}
	}

	/**
	 * Get all slot with a certain name
	 * @param slotname The name of the slots
	 * @return A <code>List</code> of all slots with that particular name
	 * @throws HibernateException When something went wrong during the database access
	 * @throws Exception When something else than the databaseaccess went wrong
	 */
	public static List<SlotDAO> getAllSlotsByName(String trackname)throws HibernateException, Exception
	{
		List<SlotDAO> tracks = new ArrayList<SlotDAO>();
		try {
			tracks = PersistenceManagerImpl.getAllTracksByName(trackname);
		} catch (HibernateException e) {
			log.error("Troubles with accessing the database when trying to get all the tracks by name: " + trackname);
			e.printStackTrace();
			throw new Exception("There were troubles accessing the Database whe trying to handle you request. Please try again");
		} catch (Exception e) {
			log.error(e.getMessage());
			e.printStackTrace();
			throw new Exception("There were problems trying to handle your request. Please try again");
		}
		return tracks;
	}

	/**
	 * Get a subset of all the slots
	 * @param startIndex Index of the first slot
	 * @param numberOfItems Number of slots that being returned
	 * @return A <code>List</code> of <code>SlotDAO</code> objects
	 * @throws HibernateException When something went wrong during the database access
	 * @throws Exception When something else than the databaseaccess went wrong
	 */
	public static List<SlotDAO> getAllSlotsFromTo(int startIndex, int numberOfItems, String howToOrder)throws HibernateException, Exception
	{
				
		List<SlotDAO> trackDaos = new ArrayList<SlotDAO>();
		
		Criteria crit;
		try {
			Session session = HibernateManager.getInstance().getSession();
			crit = session.createCriteria(SlotDAO.class);
			crit.setFirstResult(startIndex);
			crit.setMaxResults(numberOfItems);
			crit.addOrder(Order.asc(howToOrder));
		} catch (HibernateException e) {
			log.error("Troubles with accessing the database when trying to get a subset of the tracks" );
			e.printStackTrace();
			throw new Exception("There were troubles accessing the Database whe trying to handle you request. Please try again");
		} catch (Exception e) {
			log.error(e.getMessage());
			e.printStackTrace();
			throw new Exception("There were problems trying to handle you request. Please try again");
		}
		
		trackDaos = (List<SlotDAO>)crit.list();
		
		return trackDaos;
	}
	
	//TODO update!!

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (id ^ (id >>> 32));
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + ((owner == null) ? 0 : owner.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SlotDAO other = (SlotDAO) obj;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (owner == null) {
			if (other.owner != null)
				return false;
		} else if (!owner.equals(other.owner))
			return false;
		return true;
	}

	public static void update(SlotVO oldTrack, SlotVO newTrack)throws Exception
	{
		try {
			PersistenceManagerImpl.updateTrack(oldTrack, newTrack);
		} catch (Exception e) {
			throw e;
		}
	}
	
	
	
//	/**
//	 * Get a certain Track from the DB specified by its ID
//	 * @param trackId
//	 * @return
//	 * @throws IllegalArgumentException if a track with this ID doesn't exist in the DB
//	 */
//	private static TrackDAO getTrackByID(long trackId) throws IllegalArgumentException
//	{
//		TrackDAO track = null;
//		
//		Session session = HibernateManager.getInstance().getSessionFactory().openSession();
//		Query q = session.createQuery("from TrackDAO track where id = :id")
//			.setLong("id", trackId);
//		track = (TrackDAO)q.uniqueResult();
//		session.close();
//		
//		if(track==null)
//		{
//			log.error("Someone is using a track that doesn't exist in the DB with id:\n" + trackId);
//			throw new IllegalArgumentException("This track does not exist");
//		}
//		return track;
//	}
	
	/*(id: auto increment) 
	owner (foreign key user_id) 
	genre (foreign key genre_id) 
	instrument (foreign key instrument_id) 
	tempo int (BPM) (0-999) 
	scale (5) varchar (C - B) 
	beatsperbar (5) (0-99)/(0-99) 
	name (50) varchar (a-z), (A-Z), (0-9), -, _, " " 
	time_created datetime 
	filename (20) varchar (timestamp+mp3) 
	directory (39) varchar hashusername+/+tracks
	*/
}
